﻿namespace PugLib.Threading
{
    // Notices: Written by Jeffrey Richter

    using System;
    using System.Threading;

    public class AsyncResultNoResult : IAsyncResult
    {
        // Fields set at construction which never change while operation is pending
        private const Int32 StateCompletedAsynchronously = 2;
        private const Int32 StateCompletedSynchronously = 1;
        private const Int32 StatePending = 0;
        private readonly AsyncCallback _callback;
        private readonly Object _asyncState;

        // Field set at construction which do change after operation completes

        // Field that may or may not get set depending on usage
        private ManualResetEvent _waitHandle;
        private Int32 _completedState;  // defaults to StatePending

        // Fields set when operation completes
        private Exception _exception;

        public AsyncResultNoResult(AsyncCallback callback, Object state)
        {
            _callback = callback;
            _asyncState = state;
        }

        public void SetAsCompleted(Exception exception, Boolean completedSynchronously)
        {
            // Passing null for exception means no error occurred; this is the common case
            _exception = exception;

            // The m_CompletedState field MUST be set prior calling the callback
            Int32 prevState = Interlocked.Exchange(ref _completedState,
                                                   completedSynchronously ? StateCompletedSynchronously : StateCompletedAsynchronously);
            if (prevState != StatePending)
            {
                throw new InvalidOperationException("You can set a result only once");
            }

            // If the event exists, set it
            if (_waitHandle != null)
            {
                _waitHandle.Set();
            }

            // If a callback method was set, call it
            if (_callback != null)
            {
                _callback(this);
            }
        }

        public void EndInvoke()
        {
            // This method assumes that only 1 thread calls EndInvoke for this object
            if (!IsCompleted)
            {
                // If the operation isn't done, wait for it
                AsyncWaitHandle.WaitOne();
                AsyncWaitHandle.Close();
                _waitHandle = null; // Allow early GC
            }

            // Operation is done: if an exception occured, throw it
            if (_exception != null)
            {
                throw _exception;
            }
        }

        #region Implementation of IAsyncResult

        public Object AsyncState
        {
            get { return _asyncState; }
        }

        public Boolean CompletedSynchronously
        {
            get { return _completedState == StateCompletedSynchronously; }
        }

        public WaitHandle AsyncWaitHandle
        {
            get
            {
                if (_waitHandle == null)
                {
                    Boolean done = IsCompleted;
                    ManualResetEvent mre = new ManualResetEvent(done);
                    if (Interlocked.CompareExchange(ref _waitHandle, mre, null) != null)
                    {
                        // Another thread created this object's event; dispose the event we just created
                        mre.Close();
                    }
                    else
                    {
                        if (!done && IsCompleted)
                        {
                            // If the operation wasn't done when we created 
                            // the event but now it is done, set the event
                            _waitHandle.Set();
                        }
                    }
                }
                return _waitHandle;
            }
        }

        public Boolean IsCompleted
        {
            get { return _completedState != StatePending; }
        }

        #endregion
    }
}